'use client'; import { use, useEffect, useState, type CSSProperties } from 'react'; import { useDonationHub } from '@/hooks/useDonationHub'; import { fetchApi } from '@/lib/utils/client'; import { CrewRankItem } from '@/types/donation'; import './style.scss'; type Props = { params: Promise<{ widgetToken: string }>; searchParams: Promise<{ [key: string]: string|string[]|undefined }>; }; type CrewStyleConfig = { id: number; crewID: number; crewName: string; title: string; theme: number; period: number; maxDisplayCount: number; isShowAmount: boolean; isShowDonationCount: boolean; isShowContributionRate: boolean; isShowMemberIcon: boolean; isActive: boolean; bgColor: string; titleFontFamily: string|null; titleFontSizePx: number; titleFontColor: string; rank1FontFamily: string|null; rank1FontSizePx: number; rank1FontColor: string; rank2FontFamily: string|null; rank2FontSizePx: number; rank2FontColor: string; rank3FontFamily: string|null; rank3FontSizePx: number; rank3FontColor: string; rowFontFamily: string|null; rowFontSizePx: number; rowFontColor: string; }; type CrewRankResponse = { list: CrewRankItem[]; totalAmount: number; }; const THEME_NAMES = ['basic', 'dark', 'minimal']; export default function CrewLeaderboardPage({ params, searchParams }: Props) { const { widgetToken } = use(params); const sp = use(searchParams); const configID = typeof sp.configID === 'string' ? parseInt(sp.configID, 10) : null; const hubUrl = process.env.NEXT_PUBLIC_API_URL + '/hubs/donation'; const { crewRanking, setCrewRanking } = useDonationHub(widgetToken, hubUrl); const [styleCfg, setStyleCfg] = useState(null); // 스타일 config 로드 useEffect(() => { const qs = configID ? `?configID=${configID}` : ''; fetchApi(`/api/widget/crew/config/${widgetToken}${qs}`, { silent: true }).then(res => { if (res.data) { setStyleCfg(res.data); } }).catch(() => {}); }, [widgetToken, configID]); // 순위 데이터 로드 (CrewMember 기반 base — 후원 없어도 0원으로 표시) useEffect(() => { const qs = configID ? `?configID=${configID}` : ''; fetchApi(`/api/widget/crew/by-token/${widgetToken}${qs}`, { silent: true }).then(res => { if (res.data?.list) { setCrewRanking(res.data.list); } }).catch(() => {}); }, [widgetToken, configID, setCrewRanking]); const themeName = styleCfg ? (THEME_NAMES[styleCfg.theme] ?? 'basic') : 'basic'; const title = styleCfg?.title ?? '크루 순위'; const isShowAmount = styleCfg?.isShowAmount ?? true; const isShowContributionRate = styleCfg?.isShowContributionRate ?? true; const isShowDonationCount = styleCfg?.isShowDonationCount ?? false; const isShowMemberIcon = styleCfg?.isShowMemberIcon ?? true; const bodyStyle = { '--crew-bg': styleCfg?.bgColor ?? '#1A1A2E' } as CSSProperties; const titleStyle = { '--crew-title-font': styleCfg?.titleFontFamily ?? 'inherit', '--crew-title-size': `${styleCfg?.titleFontSizePx ?? 18}px`, '--crew-title-color': styleCfg?.titleFontColor ?? '#FFFFFF' } as CSSProperties; const getFontStyle = (rank: number): CSSProperties => { if (rank === 1 && styleCfg) { return { '--row-font-family': styleCfg.rank1FontFamily ?? 'inherit', '--row-font-size': `${styleCfg.rank1FontSizePx}px`, '--row-font-color': styleCfg.rank1FontColor } as CSSProperties; } if (rank === 2 && styleCfg) { return { '--row-font-family': styleCfg.rank2FontFamily ?? 'inherit', '--row-font-size': `${styleCfg.rank2FontSizePx}px`, '--row-font-color': styleCfg.rank2FontColor } as CSSProperties; } if (rank === 3 && styleCfg) { return { '--row-font-family': styleCfg.rank3FontFamily ?? 'inherit', '--row-font-size': `${styleCfg.rank3FontSizePx}px`, '--row-font-color': styleCfg.rank3FontColor } as CSSProperties; } return { '--row-font-family': styleCfg?.rowFontFamily ?? 'inherit', '--row-font-size': `${styleCfg?.rowFontSizePx ?? 14}px`, '--row-font-color': styleCfg?.rowFontColor ?? '#FFFFFF' } as CSSProperties; }; const getBadgeClass = (rank: number) => rank <= 3 ? `crew-widget__badge--${rank}` : 'crew-widget__badge--default'; return (
{title}
순위 {isShowMemberIcon && } 이름 {isShowContributionRate && 기여도} {isShowAmount && 후원 점수} {isShowDonationCount && 건수}
{crewRanking.map(item => { const style = getFontStyle(item.rank); return (
{item.rank} {isShowMemberIcon && ( item.icon ? {item.nickname} : )} {item.nickname} {isShowContributionRate && ( {item.contributionRate.toFixed(1)}% )} {isShowAmount && ( {item.totalAmount.toLocaleString()}원 )} {isShowDonationCount && ( {item.donationCount}건 )}
); })}
); }